// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/loader/throttling_resource_handler.h"

#include <memory>
#include <vector>

#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
#include "content/browser/loader/mock_resource_loader.h"
#include "content/browser/loader/resource_request_info_impl.h"
#include "content/browser/loader/test_resource_handler.h"
#include "content/public/browser/resource_throttle.h"
#include "content/public/common/resource_response.h"
#include "net/base/request_priority.h"
#include "net/url_request/redirect_info.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"

namespace content {
namespace {

    const char kInitialUrl[] = "http://initial/";
    const char kRedirectUrl[] = "http://redirect/";

    class TestResourceThrottle : public ResourceThrottle {
    public:
        explicit TestResourceThrottle(TestResourceThrottle* previous_throttle)
        {
            if (previous_throttle) {
                DCHECK(!previous_throttle->next_throttle_);
                previous_throttle_ = previous_throttle;
                previous_throttle_->next_throttle_ = this;
            }
        }

        ~TestResourceThrottle() override { }

        // Sets the throttle after this one, to enable checks that they're called in
        // the expected order.
        void SetNextThrottle(TestResourceThrottle* throttle)
        {
            DCHECK(!next_throttle_);
            DCHECK(!throttle->previous_throttle_);

            next_throttle_ = throttle;
            throttle->previous_throttle_ = this;
        }

        // ResourceThrottle implemenation:

        void WillStartRequest(bool* defer) override
        {
            EXPECT_EQ(0, will_start_request_called_);
            EXPECT_EQ(0, will_redirect_request_called_);
            EXPECT_EQ(0, will_process_response_called_);

            if (previous_throttle_) {
                EXPECT_EQ(1, previous_throttle_->will_start_request_called_);
                EXPECT_EQ(0, previous_throttle_->will_redirect_request_called_);
                EXPECT_EQ(0, previous_throttle_->will_process_response_called_);
            }

            if (next_throttle_) {
                EXPECT_EQ(0, next_throttle_->will_start_request_called_);
                EXPECT_EQ(0, next_throttle_->will_redirect_request_called_);
                EXPECT_EQ(0, next_throttle_->will_process_response_called_);
            }

            ++will_start_request_called_;
            *defer = defer_on_will_start_request_;
            if (cancel_on_will_start_request_)
                CancelWithError(net::ERR_UNEXPECTED);
        }

        void WillRedirectRequest(const net::RedirectInfo& redirect_info,
            bool* defer) override
        {
            EXPECT_EQ(GURL(kRedirectUrl), redirect_info.new_url);

            EXPECT_EQ(1, will_start_request_called_);
            // None of these tests use multiple redirects.
            EXPECT_EQ(0, will_redirect_request_called_);
            EXPECT_EQ(0, will_process_response_called_);

            if (previous_throttle_) {
                EXPECT_EQ(1, previous_throttle_->will_start_request_called_);
                EXPECT_EQ(1, previous_throttle_->will_redirect_request_called_);
                EXPECT_EQ(0, previous_throttle_->will_process_response_called_);
            }

            if (next_throttle_) {
                EXPECT_EQ(1, next_throttle_->will_start_request_called_);
                EXPECT_EQ(0, next_throttle_->will_redirect_request_called_);
                EXPECT_EQ(0, next_throttle_->will_process_response_called_);
            }

            ++will_redirect_request_called_;
            *defer = defer_on_will_redirect_request_;
            if (cancel_on_will_redirect_request_)
                CancelWithError(net::ERR_UNEXPECTED);
        }

        void WillProcessResponse(bool* defer) override
        {
            EXPECT_EQ(0, will_process_response_called_);

            if (previous_throttle_)
                EXPECT_EQ(1, previous_throttle_->will_process_response_called_);

            if (next_throttle_)
                EXPECT_EQ(0, next_throttle_->will_process_response_called_);

            ++will_process_response_called_;
            *defer = defer_on_will_process_response_;
            if (cancel_on_will_process_response_)
                CancelWithError(net::ERR_UNEXPECTED);
        }

        const char* GetNameForLogging() const override { return "Hank"; }

        int will_start_request_called() const { return will_start_request_called_; }
        int will_redirect_request_called() const
        {
            return will_redirect_request_called_;
        }
        int will_process_response_called() const
        {
            return will_process_response_called_;
        }

        void set_defer_on_will_start_request(bool defer_on_will_start_request)
        {
            defer_on_will_start_request_ = defer_on_will_start_request;
        }
        void set_defer_on_will_redirect_request(bool defer_on_will_redirect_request)
        {
            defer_on_will_redirect_request_ = defer_on_will_redirect_request;
        }
        void set_defer_on_will_process_response(bool defer_on_will_process_response)
        {
            defer_on_will_process_response_ = defer_on_will_process_response;
        }

        void set_cancel_on_will_start_request(bool cancel_on_will_start_request)
        {
            cancel_on_will_start_request_ = cancel_on_will_start_request;
        }
        void set_cancel_on_will_redirect_request(
            bool cancel_on_will_redirect_request)
        {
            cancel_on_will_redirect_request_ = cancel_on_will_redirect_request;
        }
        void set_cancel_on_will_process_response(
            bool cancel_on_will_process_response)
        {
            cancel_on_will_process_response_ = cancel_on_will_process_response;
        }

        using ResourceThrottle::CancelWithError;
        using ResourceThrottle::Resume;

    private:
        int will_start_request_called_ = 0;
        int will_redirect_request_called_ = 0;
        int will_process_response_called_ = 0;

        bool defer_on_will_start_request_ = false;
        bool defer_on_will_redirect_request_ = false;
        bool defer_on_will_process_response_ = false;

        bool cancel_on_will_start_request_ = false;
        bool cancel_on_will_redirect_request_ = false;
        bool cancel_on_will_process_response_ = false;

        TestResourceThrottle* previous_throttle_ = nullptr;
        TestResourceThrottle* next_throttle_ = nullptr;

        DISALLOW_COPY_AND_ASSIGN(TestResourceThrottle);
    };

    class ThrottlingResourceHandlerTest : public testing::Test {
    public:
        ThrottlingResourceHandlerTest()
            : never_started_url_request_(request_context_.CreateRequest(
                GURL(kInitialUrl),
                net::DEFAULT_PRIORITY,
                &never_started_url_request_delegate_))
            , throttle1_(new TestResourceThrottle(nullptr))
            , throttle2_(new TestResourceThrottle(throttle1_))
            , test_handler_(new TestResourceHandler())
        {
            std::vector<std::unique_ptr<ResourceThrottle>> throttles;
            throttles.push_back(base::WrapUnique(throttle1_));
            throttles.push_back(base::WrapUnique(throttle2_));
            throttling_handler_.reset(new ThrottlingResourceHandler(
                base::WrapUnique(test_handler_), never_started_url_request_.get(),
                std::move(throttles)));
            mock_loader_.reset(new MockResourceLoader(throttling_handler_.get()));

            // Basic initial state sanity checks.
            EXPECT_EQ(0, test_handler_->on_will_start_called());
            EXPECT_EQ(0, throttle1_->will_start_request_called());
            EXPECT_EQ(0, throttle2_->will_start_request_called());
        }

        // Finish the request with a 0-byte read and success.  Reads are not passed
        // to ResourceThrottles, so are uninteresting for the purposes of these tests.
        void FinishRequestSuccessfully()
        {
            EXPECT_EQ(0, test_handler_->on_will_read_called());

            ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead(1));
            EXPECT_EQ(1, test_handler_->on_will_read_called());
            EXPECT_EQ(0, test_handler_->on_read_completed_called());

            ASSERT_EQ(MockResourceLoader::Status::IDLE,
                mock_loader_->OnReadCompleted(0));
            EXPECT_EQ(1, test_handler_->on_read_completed_called());
            EXPECT_EQ(0, test_handler_->on_response_completed_called());

            ASSERT_EQ(MockResourceLoader::Status::IDLE,
                mock_loader_->OnResponseCompleted(
                    net::URLRequestStatus::FromError(net::OK)));
            EXPECT_EQ(net::OK, mock_loader_->error_code());
            EXPECT_EQ(1, test_handler_->on_read_completed_called());
            EXPECT_EQ(1, test_handler_->on_response_completed_called());
        }

    protected:
        // Needs to be first, so it's destroyed last.
        base::MessageLoopForIO message_loop_;

        // Machinery to construct a URLRequest that's just used as an argument to
        // methods that expect one, and is never actually started.
        net::TestURLRequestContext request_context_;
        net::TestDelegate never_started_url_request_delegate_;
        std::unique_ptr<net::URLRequest> never_started_url_request_;

        // Owned by test_handler_;
        TestResourceThrottle* throttle1_;
        TestResourceThrottle* throttle2_;

        // Owned by |throttling_handler_|.
        TestResourceHandler* test_handler_;
        std::unique_ptr<ThrottlingResourceHandler> throttling_handler_;
        std::unique_ptr<MockResourceLoader> mock_loader_;

    private:
        DISALLOW_COPY_AND_ASSIGN(ThrottlingResourceHandlerTest);
    };

    TEST_F(ThrottlingResourceHandlerTest, Sync)
    {
        ASSERT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnWillStart(GURL(kInitialUrl)));
        EXPECT_EQ(1, throttle1_->will_start_request_called());
        EXPECT_EQ(0, throttle1_->will_redirect_request_called());

        EXPECT_EQ(1, throttle2_->will_start_request_called());

        EXPECT_EQ(1, test_handler_->on_will_start_called());
        EXPECT_EQ(0, test_handler_->on_request_redirected_called());

        net::RedirectInfo redirect_info;
        redirect_info.status_code = 301;
        redirect_info.new_url = GURL(kRedirectUrl);
        ASSERT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnRequestRedirected(
                redirect_info, make_scoped_refptr(new ResourceResponse())));
        EXPECT_EQ(1, throttle1_->will_redirect_request_called());
        EXPECT_EQ(0, throttle1_->will_process_response_called());

        EXPECT_EQ(1, throttle2_->will_redirect_request_called());

        EXPECT_EQ(1, test_handler_->on_request_redirected_called());
        EXPECT_EQ(0, test_handler_->on_response_started_called());

        ASSERT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnResponseStarted(
                make_scoped_refptr(new ResourceResponse())));
        EXPECT_EQ(1, throttle1_->will_process_response_called());
        EXPECT_EQ(1, throttle2_->will_process_response_called());

        EXPECT_EQ(1, test_handler_->on_request_redirected_called());
        EXPECT_EQ(1, test_handler_->on_response_started_called());
        EXPECT_EQ(0, test_handler_->on_read_completed_called());

        FinishRequestSuccessfully();
    }

    TEST_F(ThrottlingResourceHandlerTest, Async)
    {
        throttle1_->set_defer_on_will_start_request(true);
        throttle1_->set_defer_on_will_redirect_request(true);
        throttle1_->set_defer_on_will_process_response(true);

        throttle2_->set_defer_on_will_start_request(true);
        throttle2_->set_defer_on_will_redirect_request(true);
        throttle2_->set_defer_on_will_process_response(true);

        ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING,
            mock_loader_->OnWillStart(GURL(kInitialUrl)));
        EXPECT_EQ(1, throttle1_->will_start_request_called());
        EXPECT_EQ(0, throttle2_->will_start_request_called());

        throttle1_->Resume();
        EXPECT_EQ(1, throttle2_->will_start_request_called());
        EXPECT_EQ(0, test_handler_->on_will_start_called());
        ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING,
            mock_loader_->status());

        throttle2_->Resume();
        EXPECT_EQ(1, test_handler_->on_will_start_called());
        ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->status());

        EXPECT_EQ(0, throttle1_->will_redirect_request_called());
        net::RedirectInfo redirect_info;
        redirect_info.status_code = 301;
        redirect_info.new_url = GURL(kRedirectUrl);
        ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING,
            mock_loader_->OnRequestRedirected(
                redirect_info, make_scoped_refptr(new ResourceResponse())));
        EXPECT_EQ(1, throttle1_->will_redirect_request_called());
        EXPECT_EQ(0, throttle2_->will_redirect_request_called());

        throttle1_->Resume();
        EXPECT_EQ(1, throttle2_->will_redirect_request_called());
        EXPECT_EQ(0, test_handler_->on_request_redirected_called());
        ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING,
            mock_loader_->status());

        throttle2_->Resume();
        EXPECT_EQ(1, test_handler_->on_request_redirected_called());
        ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->status());

        EXPECT_EQ(0, throttle1_->will_process_response_called());
        ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING,
            mock_loader_->OnResponseStarted(
                make_scoped_refptr(new ResourceResponse())));
        EXPECT_EQ(1, throttle1_->will_process_response_called());
        EXPECT_EQ(0, throttle2_->will_process_response_called());

        throttle1_->Resume();
        EXPECT_EQ(1, throttle2_->will_process_response_called());
        EXPECT_EQ(0, test_handler_->on_response_started_called());
        ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING,
            mock_loader_->status());

        throttle2_->Resume();
        EXPECT_EQ(1, test_handler_->on_request_redirected_called());
        EXPECT_EQ(1, test_handler_->on_response_started_called());
        EXPECT_EQ(0, test_handler_->on_read_completed_called());
        ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->status());

        FinishRequestSuccessfully();
    }

    // For a given method (WillStartRequest, WillRedirectRequest,
    // WillProcessResponse), each of the two throttles can cancel asynchronously or
    // synchronously, and the second throttle can cancel synchronously or
    // asynchronously after the first throttle completes synchronously or
    // asynchronously, for a total of 6 combinations where one of the
    // ResourceThrottle cancels in each phase.  However:
    // 1)  Whenever the second throttle cancels asynchronously, it doesn't matter if
    // the first one completed synchronously or asynchronously, the state when it
    // cancels is the same.
    // 2)  The second cancelling asynchronously is much like the first one
    // cancelling asynchronously, so isn't worth testing individually.
    // 3)  Similarly, the second cancelling synchronously after the first one
    // completes synchronously doesn't really add anything to the first cancelling
    // synchronously.  The case where the second cancels synchronously after the
    // first completes asynchronously is more interesting - the cancellation happens
    // in a Resume() call rather in the initial WillFoo call.
    // So that leaves 3 interesting test cases for each of the three points
    // throttles can cancel.

    TEST_F(ThrottlingResourceHandlerTest, FirstThrottleSyncCancelOnWillStart)
    {
        throttle1_->set_cancel_on_will_start_request(true);

        ASSERT_EQ(MockResourceLoader::Status::CANCELED,
            mock_loader_->OnWillStart(GURL(kInitialUrl)));
        EXPECT_EQ(1, throttle1_->will_start_request_called());
        EXPECT_EQ(0, throttle2_->will_start_request_called());
        EXPECT_EQ(0, test_handler_->on_will_start_called());
        ASSERT_EQ(net::ERR_UNEXPECTED, mock_loader_->error_code());

        // The MockResourceLoader now informs the ResourceHandler of cancellation.
        ASSERT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnResponseCompleted(
                net::URLRequestStatus::FromError(net::ERR_UNEXPECTED)));

        EXPECT_EQ(1, throttle1_->will_start_request_called());
        EXPECT_EQ(0, throttle1_->will_redirect_request_called());
        EXPECT_EQ(0, throttle1_->will_process_response_called());

        EXPECT_EQ(0, throttle2_->will_start_request_called());
        EXPECT_EQ(0, throttle2_->will_redirect_request_called());
        EXPECT_EQ(0, throttle2_->will_process_response_called());

        EXPECT_EQ(0, test_handler_->on_will_start_called());
        EXPECT_EQ(0, test_handler_->on_request_redirected_called());
        EXPECT_EQ(0, test_handler_->on_response_started_called());
        EXPECT_EQ(1, test_handler_->on_response_completed_called());
    }

    TEST_F(ThrottlingResourceHandlerTest, FirstThrottleAsyncCancelOnWillStart)
    {
        throttle1_->set_defer_on_will_start_request(true);

        ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING,
            mock_loader_->OnWillStart(GURL(kInitialUrl)));
        EXPECT_EQ(1, throttle1_->will_start_request_called());
        EXPECT_EQ(0, throttle2_->will_start_request_called());
        EXPECT_EQ(0, test_handler_->on_will_start_called());

        throttle1_->CancelWithError(net::ERR_UNEXPECTED);
        EXPECT_EQ(1, throttle1_->will_start_request_called());
        EXPECT_EQ(0, throttle2_->will_start_request_called());
        EXPECT_EQ(0, test_handler_->on_will_start_called());
        ASSERT_EQ(MockResourceLoader::Status::CANCELED, mock_loader_->status());
        ASSERT_EQ(net::ERR_UNEXPECTED, mock_loader_->error_code());

        // The MockResourceLoader now informs the ResourceHandler of cancellation.
        ASSERT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnResponseCompleted(
                net::URLRequestStatus::FromError(net::ERR_UNEXPECTED)));

        EXPECT_EQ(1, throttle1_->will_start_request_called());
        EXPECT_EQ(0, throttle1_->will_redirect_request_called());
        EXPECT_EQ(0, throttle1_->will_process_response_called());

        EXPECT_EQ(0, throttle2_->will_start_request_called());
        EXPECT_EQ(0, throttle2_->will_redirect_request_called());
        EXPECT_EQ(0, throttle2_->will_process_response_called());

        EXPECT_EQ(0, test_handler_->on_will_start_called());
        EXPECT_EQ(0, test_handler_->on_request_redirected_called());
        EXPECT_EQ(0, test_handler_->on_response_started_called());
        EXPECT_EQ(1, test_handler_->on_response_completed_called());
    }

    // The first throttle also defers and then resumes the request, so that this
    // cancel happens with Resume() on the top of the callstack, instead of
    // OnWillStart(), unlike the test where the first throttle synchronously
    // cancels.
    TEST_F(ThrottlingResourceHandlerTest, SecondThrottleSyncCancelOnWillStart)
    {
        throttle1_->set_defer_on_will_start_request(true);
        throttle2_->set_cancel_on_will_start_request(true);

        ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING,
            mock_loader_->OnWillStart(GURL(kInitialUrl)));
        EXPECT_EQ(1, throttle1_->will_start_request_called());
        EXPECT_EQ(0, throttle2_->will_start_request_called());
        EXPECT_EQ(0, test_handler_->on_will_start_called());

        throttle1_->Resume();
        EXPECT_EQ(1, throttle1_->will_start_request_called());
        EXPECT_EQ(1, throttle2_->will_start_request_called());
        EXPECT_EQ(0, test_handler_->on_will_start_called());
        ASSERT_EQ(MockResourceLoader::Status::CANCELED, mock_loader_->status());
        ASSERT_EQ(net::ERR_UNEXPECTED, mock_loader_->error_code());

        // The MockResourceLoader now informs the ResourceHandler of cancellation.
        ASSERT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnResponseCompleted(
                net::URLRequestStatus::FromError(net::ERR_UNEXPECTED)));

        EXPECT_EQ(1, throttle1_->will_start_request_called());
        EXPECT_EQ(0, throttle1_->will_redirect_request_called());
        EXPECT_EQ(0, throttle1_->will_process_response_called());

        EXPECT_EQ(1, throttle2_->will_start_request_called());
        EXPECT_EQ(0, throttle2_->will_redirect_request_called());
        EXPECT_EQ(0, throttle2_->will_process_response_called());

        EXPECT_EQ(0, test_handler_->on_will_start_called());
        EXPECT_EQ(0, test_handler_->on_request_redirected_called());
        EXPECT_EQ(0, test_handler_->on_response_started_called());
        EXPECT_EQ(1, test_handler_->on_response_completed_called());
    }

    TEST_F(ThrottlingResourceHandlerTest,
        FirstThrottleSyncCancelOnRequestRedirected)
    {
        throttle1_->set_cancel_on_will_redirect_request(true);

        ASSERT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnWillStart(GURL(kInitialUrl)));

        net::RedirectInfo redirect_info;
        redirect_info.status_code = 301;
        redirect_info.new_url = GURL(kRedirectUrl);
        ASSERT_EQ(MockResourceLoader::Status::CANCELED,
            mock_loader_->OnRequestRedirected(
                redirect_info, make_scoped_refptr(new ResourceResponse())));
        EXPECT_EQ(1, throttle1_->will_redirect_request_called());
        EXPECT_EQ(0, throttle2_->will_redirect_request_called());
        EXPECT_EQ(0, test_handler_->on_request_redirected_called());
        ASSERT_EQ(net::ERR_UNEXPECTED, mock_loader_->error_code());

        // The MockResourceLoader now informs the ResourceHandler of cancellation.
        ASSERT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnResponseCompleted(
                net::URLRequestStatus::FromError(net::ERR_UNEXPECTED)));

        EXPECT_EQ(1, throttle1_->will_start_request_called());
        EXPECT_EQ(1, throttle1_->will_redirect_request_called());
        EXPECT_EQ(0, throttle1_->will_process_response_called());

        EXPECT_EQ(1, throttle2_->will_start_request_called());
        EXPECT_EQ(0, throttle2_->will_redirect_request_called());
        EXPECT_EQ(0, throttle2_->will_process_response_called());

        EXPECT_EQ(1, test_handler_->on_will_start_called());
        EXPECT_EQ(0, test_handler_->on_request_redirected_called());
        EXPECT_EQ(0, test_handler_->on_response_started_called());
        EXPECT_EQ(1, test_handler_->on_response_completed_called());
    }

    TEST_F(ThrottlingResourceHandlerTest,
        FirstThrottleAsyncCancelOnRequestRedirected)
    {
        throttle1_->set_defer_on_will_redirect_request(true);

        ASSERT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnWillStart(GURL(kInitialUrl)));

        net::RedirectInfo redirect_info;
        redirect_info.status_code = 301;
        redirect_info.new_url = GURL(kRedirectUrl);
        ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING,
            mock_loader_->OnRequestRedirected(
                redirect_info, make_scoped_refptr(new ResourceResponse())));

        throttle1_->CancelWithError(net::ERR_UNEXPECTED);
        EXPECT_EQ(1, throttle1_->will_redirect_request_called());
        EXPECT_EQ(0, throttle2_->will_redirect_request_called());
        EXPECT_EQ(0, test_handler_->on_request_redirected_called());
        ASSERT_EQ(MockResourceLoader::Status::CANCELED, mock_loader_->status());
        ASSERT_EQ(net::ERR_UNEXPECTED, mock_loader_->error_code());

        // The MockResourceLoader now informs the ResourceHandler of cancellation.
        ASSERT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnResponseCompleted(
                net::URLRequestStatus::FromError(net::ERR_UNEXPECTED)));

        EXPECT_EQ(1, throttle1_->will_start_request_called());
        EXPECT_EQ(1, throttle1_->will_redirect_request_called());
        EXPECT_EQ(0, throttle1_->will_process_response_called());

        EXPECT_EQ(1, throttle2_->will_start_request_called());
        EXPECT_EQ(0, throttle2_->will_redirect_request_called());
        EXPECT_EQ(0, throttle2_->will_process_response_called());

        EXPECT_EQ(1, test_handler_->on_will_start_called());
        EXPECT_EQ(0, test_handler_->on_request_redirected_called());
        EXPECT_EQ(0, test_handler_->on_response_started_called());
        EXPECT_EQ(1, test_handler_->on_response_completed_called());
    }

    // The first throttle also defers and then resumes the request, so that this
    // cancel happens with Resume() on the top of the callstack, instead of
    // OnRequestRedirected(), unlike the test where the first throttle synchronously
    // cancels.
    TEST_F(ThrottlingResourceHandlerTest,
        SecondThrottleSyncCancelOnRequestRedirected)
    {
        throttle1_->set_defer_on_will_redirect_request(true);
        throttle2_->set_cancel_on_will_redirect_request(true);

        ASSERT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnWillStart(GURL(kInitialUrl)));

        net::RedirectInfo redirect_info;
        redirect_info.status_code = 301;
        redirect_info.new_url = GURL(kRedirectUrl);
        ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING,
            mock_loader_->OnRequestRedirected(
                redirect_info, make_scoped_refptr(new ResourceResponse())));

        throttle1_->Resume();
        EXPECT_EQ(1, throttle1_->will_redirect_request_called());
        EXPECT_EQ(1, throttle2_->will_redirect_request_called());
        EXPECT_EQ(0, test_handler_->on_request_redirected_called());
        ASSERT_EQ(MockResourceLoader::Status::CANCELED, mock_loader_->status());
        ASSERT_EQ(net::ERR_UNEXPECTED, mock_loader_->error_code());

        // The MockResourceLoader now informs the ResourceHandler of cancellation.
        ASSERT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnResponseCompleted(
                net::URLRequestStatus::FromError(net::ERR_UNEXPECTED)));

        EXPECT_EQ(1, throttle1_->will_start_request_called());
        EXPECT_EQ(1, throttle1_->will_redirect_request_called());
        EXPECT_EQ(0, throttle1_->will_process_response_called());

        EXPECT_EQ(1, throttle2_->will_start_request_called());
        EXPECT_EQ(1, throttle2_->will_redirect_request_called());
        EXPECT_EQ(0, throttle2_->will_process_response_called());

        EXPECT_EQ(1, test_handler_->on_will_start_called());
        EXPECT_EQ(0, test_handler_->on_request_redirected_called());
        EXPECT_EQ(0, test_handler_->on_response_started_called());
        EXPECT_EQ(1, test_handler_->on_response_completed_called());
    }

    TEST_F(ThrottlingResourceHandlerTest,
        FirstThrottleSyncCancelOnWillProcessResponse)
    {
        throttle1_->set_cancel_on_will_process_response(true);

        ASSERT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnWillStart(GURL(kInitialUrl)));

        ASSERT_EQ(MockResourceLoader::Status::CANCELED,
            mock_loader_->OnResponseStarted(
                make_scoped_refptr(new ResourceResponse())));
        EXPECT_EQ(1, throttle1_->will_process_response_called());
        EXPECT_EQ(0, throttle2_->will_process_response_called());
        EXPECT_EQ(0, test_handler_->on_response_started_called());
        ASSERT_EQ(net::ERR_UNEXPECTED, mock_loader_->error_code());

        // The MockResourceLoader now informs the ResourceHandler of cancellation.
        ASSERT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnResponseCompleted(
                net::URLRequestStatus::FromError(net::ERR_UNEXPECTED)));

        EXPECT_EQ(1, throttle1_->will_start_request_called());
        EXPECT_EQ(0, throttle1_->will_redirect_request_called());
        EXPECT_EQ(1, throttle1_->will_process_response_called());

        EXPECT_EQ(1, throttle2_->will_start_request_called());
        EXPECT_EQ(0, throttle2_->will_redirect_request_called());
        EXPECT_EQ(0, throttle2_->will_process_response_called());

        EXPECT_EQ(1, test_handler_->on_will_start_called());
        EXPECT_EQ(0, test_handler_->on_request_redirected_called());
        EXPECT_EQ(0, test_handler_->on_response_started_called());
        EXPECT_EQ(1, test_handler_->on_response_completed_called());
    }

    TEST_F(ThrottlingResourceHandlerTest,
        FirstThrottleAsyncCancelOnWillProcessResponse)
    {
        throttle1_->set_defer_on_will_process_response(true);

        ASSERT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnWillStart(GURL(kInitialUrl)));

        ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING,
            mock_loader_->OnResponseStarted(
                make_scoped_refptr(new ResourceResponse())));
        EXPECT_EQ(1, throttle1_->will_process_response_called());
        EXPECT_EQ(0, throttle2_->will_process_response_called());
        EXPECT_EQ(0, test_handler_->on_response_started_called());

        throttle1_->CancelWithError(net::ERR_UNEXPECTED);
        EXPECT_EQ(1, throttle1_->will_process_response_called());
        EXPECT_EQ(0, throttle2_->will_process_response_called());
        EXPECT_EQ(0, test_handler_->on_response_started_called());
        ASSERT_EQ(MockResourceLoader::Status::CANCELED, mock_loader_->status());
        ASSERT_EQ(net::ERR_UNEXPECTED, mock_loader_->error_code());

        // The MockResourceLoader now informs the ResourceHandler of cancellation.
        ASSERT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnResponseCompleted(
                net::URLRequestStatus::FromError(net::ERR_UNEXPECTED)));

        EXPECT_EQ(1, throttle1_->will_start_request_called());
        EXPECT_EQ(0, throttle1_->will_redirect_request_called());
        EXPECT_EQ(1, throttle1_->will_process_response_called());

        EXPECT_EQ(1, throttle2_->will_start_request_called());
        EXPECT_EQ(0, throttle2_->will_redirect_request_called());
        EXPECT_EQ(0, throttle2_->will_process_response_called());

        EXPECT_EQ(1, test_handler_->on_will_start_called());
        EXPECT_EQ(0, test_handler_->on_request_redirected_called());
        EXPECT_EQ(0, test_handler_->on_response_started_called());
        EXPECT_EQ(1, test_handler_->on_response_completed_called());
    }

    // The first throttle also defers and then resumes the request, so that this
    // cancel happens with Resume() on the top of the callstack, instead of
    // OnWillProcessResponse(), unlike the test where the first throttle
    // synchronously cancels.
    TEST_F(ThrottlingResourceHandlerTest,
        SecondThrottleSyncCancelOnWillProcessResponse)
    {
        throttle1_->set_defer_on_will_process_response(true);
        throttle2_->set_cancel_on_will_process_response(true);

        ASSERT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnWillStart(GURL(kInitialUrl)));

        ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING,
            mock_loader_->OnResponseStarted(
                make_scoped_refptr(new ResourceResponse())));
        EXPECT_EQ(1, throttle1_->will_process_response_called());
        EXPECT_EQ(0, throttle2_->will_process_response_called());
        EXPECT_EQ(0, test_handler_->on_response_started_called());

        throttle1_->Resume();
        EXPECT_EQ(1, throttle1_->will_process_response_called());
        EXPECT_EQ(1, throttle2_->will_process_response_called());
        EXPECT_EQ(0, test_handler_->on_response_started_called());
        ASSERT_EQ(MockResourceLoader::Status::CANCELED, mock_loader_->status());
        ASSERT_EQ(net::ERR_UNEXPECTED, mock_loader_->error_code());

        // The MockResourceLoader now informs the ResourceHandler of cancellation.
        ASSERT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnResponseCompleted(
                net::URLRequestStatus::FromError(net::ERR_UNEXPECTED)));

        EXPECT_EQ(1, throttle1_->will_start_request_called());
        EXPECT_EQ(0, throttle1_->will_redirect_request_called());
        EXPECT_EQ(1, throttle1_->will_process_response_called());

        EXPECT_EQ(1, throttle2_->will_start_request_called());
        EXPECT_EQ(0, throttle2_->will_redirect_request_called());
        EXPECT_EQ(1, throttle2_->will_process_response_called());

        EXPECT_EQ(1, test_handler_->on_will_start_called());
        EXPECT_EQ(0, test_handler_->on_request_redirected_called());
        EXPECT_EQ(0, test_handler_->on_response_started_called());
        EXPECT_EQ(1, test_handler_->on_response_completed_called());
    }

    TEST_F(ThrottlingResourceHandlerTest, OutOfBandCancelBeforeWillStart)
    {
        throttle1_->CancelWithError(net::ERR_UNEXPECTED);
        EXPECT_EQ(MockResourceLoader::Status::CANCELED, mock_loader_->status());
        EXPECT_EQ(net::ERR_UNEXPECTED, mock_loader_->error_code());

        // The MockResourceLoader now informs the ResourceHandler of cancellation.
        EXPECT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnResponseCompleted(
                net::URLRequestStatus::FromError(net::ERR_UNEXPECTED)));

        EXPECT_EQ(0, throttle1_->will_start_request_called());
        EXPECT_EQ(0, throttle1_->will_redirect_request_called());
        EXPECT_EQ(0, throttle1_->will_process_response_called());

        EXPECT_EQ(0, throttle2_->will_start_request_called());
        EXPECT_EQ(0, throttle2_->will_redirect_request_called());
        EXPECT_EQ(0, throttle2_->will_process_response_called());

        EXPECT_EQ(0, test_handler_->on_will_start_called());
        EXPECT_EQ(0, test_handler_->on_request_redirected_called());
        EXPECT_EQ(0, test_handler_->on_response_started_called());
        EXPECT_EQ(1, test_handler_->on_response_completed_called());
    }

    TEST_F(ThrottlingResourceHandlerTest, OutOfBandCancelAfterWillStart)
    {
        EXPECT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnWillStart(GURL(kInitialUrl)));

        throttle1_->CancelWithError(net::ERR_UNEXPECTED);
        EXPECT_EQ(MockResourceLoader::Status::CANCELED, mock_loader_->status());
        EXPECT_EQ(net::ERR_UNEXPECTED, mock_loader_->error_code());

        // The MockResourceLoader now informs the ResourceHandler of cancellation.
        EXPECT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnResponseCompleted(
                net::URLRequestStatus::FromError(net::ERR_UNEXPECTED)));

        EXPECT_EQ(1, throttle1_->will_start_request_called());
        EXPECT_EQ(0, throttle1_->will_redirect_request_called());
        EXPECT_EQ(0, throttle1_->will_process_response_called());

        EXPECT_EQ(1, throttle2_->will_start_request_called());
        EXPECT_EQ(0, throttle2_->will_redirect_request_called());
        EXPECT_EQ(0, throttle2_->will_process_response_called());

        EXPECT_EQ(1, test_handler_->on_will_start_called());
        EXPECT_EQ(0, test_handler_->on_request_redirected_called());
        EXPECT_EQ(0, test_handler_->on_response_started_called());
        EXPECT_EQ(1, test_handler_->on_response_completed_called());
    }

    TEST_F(ThrottlingResourceHandlerTest, OutOfBandCancelAfterRequestRedirected)
    {
        EXPECT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnWillStart(GURL(kInitialUrl)));
        net::RedirectInfo redirect_info;
        redirect_info.status_code = 301;
        redirect_info.new_url = GURL(kRedirectUrl);
        EXPECT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnRequestRedirected(
                redirect_info, make_scoped_refptr(new ResourceResponse())));

        throttle1_->CancelWithError(net::ERR_UNEXPECTED);
        EXPECT_EQ(MockResourceLoader::Status::CANCELED, mock_loader_->status());
        EXPECT_EQ(net::ERR_UNEXPECTED, mock_loader_->error_code());

        // The MockResourceLoader now informs the ResourceHandler of cancellation.
        EXPECT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnResponseCompleted(
                net::URLRequestStatus::FromError(net::ERR_UNEXPECTED)));

        EXPECT_EQ(1, throttle1_->will_start_request_called());
        EXPECT_EQ(1, throttle1_->will_redirect_request_called());
        EXPECT_EQ(0, throttle1_->will_process_response_called());

        EXPECT_EQ(1, throttle2_->will_start_request_called());
        EXPECT_EQ(1, throttle2_->will_redirect_request_called());
        EXPECT_EQ(0, throttle2_->will_process_response_called());

        EXPECT_EQ(1, test_handler_->on_will_start_called());
        EXPECT_EQ(1, test_handler_->on_request_redirected_called());
        EXPECT_EQ(0, test_handler_->on_response_started_called());
        EXPECT_EQ(1, test_handler_->on_response_completed_called());
    }

    TEST_F(ThrottlingResourceHandlerTest, OutOfBandCancelAfterResponseStarted)
    {
        EXPECT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnWillStart(GURL(kInitialUrl)));
        net::RedirectInfo redirect_info;
        redirect_info.status_code = 301;
        redirect_info.new_url = GURL(kRedirectUrl);
        EXPECT_EQ(
            MockResourceLoader::Status::IDLE,
            mock_loader_->OnRequestRedirected(redirect_info, new ResourceResponse()));
        EXPECT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnResponseStarted(
                make_scoped_refptr(new ResourceResponse())));

        throttle1_->CancelWithError(net::ERR_UNEXPECTED);
        EXPECT_EQ(MockResourceLoader::Status::CANCELED, mock_loader_->status());
        EXPECT_EQ(net::ERR_UNEXPECTED, mock_loader_->error_code());

        // The MockResourceLoader now informs the ResourceHandler of cancellation.
        EXPECT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnResponseCompleted(
                net::URLRequestStatus::FromError(net::ERR_UNEXPECTED)));

        EXPECT_EQ(1, throttle1_->will_start_request_called());
        EXPECT_EQ(1, throttle1_->will_redirect_request_called());
        EXPECT_EQ(1, throttle1_->will_process_response_called());

        EXPECT_EQ(1, throttle2_->will_start_request_called());
        EXPECT_EQ(1, throttle2_->will_redirect_request_called());
        EXPECT_EQ(1, throttle2_->will_process_response_called());

        EXPECT_EQ(1, test_handler_->on_will_start_called());
        EXPECT_EQ(1, test_handler_->on_request_redirected_called());
        EXPECT_EQ(1, test_handler_->on_response_started_called());
        EXPECT_EQ(1, test_handler_->on_response_completed_called());
    }

    TEST_F(ThrottlingResourceHandlerTest, OutOfBandCancelAndResumeDuringWillStart)
    {
        throttle1_->set_defer_on_will_start_request(1);
        EXPECT_EQ(MockResourceLoader::Status::CALLBACK_PENDING,
            mock_loader_->OnWillStart(GURL(kInitialUrl)));

        // |throttle2_| cancels.
        throttle2_->CancelWithError(net::ERR_UNEXPECTED);
        EXPECT_EQ(MockResourceLoader::Status::CANCELED, mock_loader_->status());
        EXPECT_EQ(net::ERR_UNEXPECTED, mock_loader_->error_code());

        // |throttle1_|, blissfully unaware of cancellation, resumes the request.
        throttle1_->Resume();

        // The MockResourceLoader now informs the ResourceHandler of cancellation.
        EXPECT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnResponseCompleted(
                net::URLRequestStatus::FromError(net::ERR_UNEXPECTED)));

        EXPECT_EQ(1, throttle1_->will_start_request_called());
        EXPECT_EQ(0, throttle1_->will_redirect_request_called());
        EXPECT_EQ(0, throttle1_->will_process_response_called());

        EXPECT_EQ(0, throttle2_->will_start_request_called());
        EXPECT_EQ(0, throttle2_->will_redirect_request_called());
        EXPECT_EQ(0, throttle2_->will_process_response_called());

        EXPECT_EQ(0, test_handler_->on_will_start_called());
        EXPECT_EQ(0, test_handler_->on_request_redirected_called());
        EXPECT_EQ(0, test_handler_->on_response_started_called());
        EXPECT_EQ(1, test_handler_->on_response_completed_called());
    }

    TEST_F(ThrottlingResourceHandlerTest, DoubleCancelDuringWillStart)
    {
        throttle1_->set_defer_on_will_start_request(1);
        EXPECT_EQ(MockResourceLoader::Status::CALLBACK_PENDING,
            mock_loader_->OnWillStart(GURL(kInitialUrl)));

        // |throttle2_| cancels.
        throttle2_->CancelWithError(net::ERR_UNEXPECTED);
        EXPECT_EQ(MockResourceLoader::Status::CANCELED, mock_loader_->status());
        EXPECT_EQ(net::ERR_UNEXPECTED, mock_loader_->error_code());

        // |throttle1_|, unaware of the cancellation, also cancels.
        throttle1_->CancelWithError(net::ERR_FAILED);
        EXPECT_EQ(MockResourceLoader::Status::CANCELED, mock_loader_->status());
        EXPECT_EQ(net::ERR_UNEXPECTED, mock_loader_->error_code());

        // The MockResourceLoader now informs the ResourceHandler of cancellation.
        EXPECT_EQ(MockResourceLoader::Status::IDLE,
            mock_loader_->OnResponseCompleted(
                net::URLRequestStatus::FromError(net::ERR_UNEXPECTED)));

        EXPECT_EQ(1, throttle1_->will_start_request_called());
        EXPECT_EQ(0, throttle1_->will_redirect_request_called());
        EXPECT_EQ(0, throttle1_->will_process_response_called());

        EXPECT_EQ(0, throttle2_->will_start_request_called());
        EXPECT_EQ(0, throttle2_->will_redirect_request_called());
        EXPECT_EQ(0, throttle2_->will_process_response_called());

        EXPECT_EQ(0, test_handler_->on_will_start_called());
        EXPECT_EQ(0, test_handler_->on_request_redirected_called());
        EXPECT_EQ(0, test_handler_->on_response_started_called());
        EXPECT_EQ(1, test_handler_->on_response_completed_called());
    }

} // namespace
} // namespace content
